package permhelp

import (
	"fmt"
	"github.com/astaxie/beego/orm"
	"io/ioutil"
	"regexp"
	"sort"
	"strconv"
	"strings"
)

//本地数据库权限自动生成
type LocalPermHelp struct {
	databasePerms  []Perm //数据库权限数据
	fileParsePerms []Perm //从控制器文件中解析出的权限数据

	controllerDirFiles    []string //控制器下文件
	exceptControllerFiles []string //设置不需要分析的文件
}

//本地数据库权限自动生成
func NewLocalPermHelp(exceptControllerFiles ...string) *LocalPermHelp {
	this := &LocalPermHelp{
		exceptControllerFiles: exceptControllerFiles,
	}

	//载入控制器下所有文件
	this.loadControllerFiles().setFileParsePerms()

	//载入数据库中权限数据
	this.loadDatabasePerms()

	return this
}

//载入controller下所有控制器文件
func (this *LocalPermHelp) loadControllerFiles() *LocalPermHelp {

	files, _ := ioutil.ReadDir(ControllerDir)

	for _, f := range files {
		if strings.Contains(f.Name(), "_test.go") || !strings.Contains(f.Name(), ".go") {
			continue
		}

		//不需要分析的文件
		if inArray(f.Name(), this.exceptControllerFiles) {
			continue
		}

		this.controllerDirFiles = append(this.controllerDirFiles, f.Name())
	}
	return this
}

//载入数据库中权限数据
func (this *LocalPermHelp) loadDatabasePerms() *LocalPermHelp {

	if _, err := orm.NewOrm().QueryTable(PermTableName).Limit(-1).All(&this.databasePerms); err != nil {
		fmt.Println("LocalPermHelp.getPerm 错误：", err)
	}

	return this
}

//通过permUrl 获取数据库中权限
func (this *LocalPermHelp) getPerm(permUrl string, perms []Perm) *Perm {

	for k := range perms {
		if perms[k].PermUrl == permUrl {
			return &perms[k]
		}
	}

	return nil
}

//计算控制文件中的权限
func (this *LocalPermHelp) setFileParsePerms() {
	for _, file := range this.controllerDirFiles {
		this.fileParsePerms = append(this.fileParsePerms, parseControllerFiles(ControllerDir+file)...)
	}

}

//从注释中解析出方法名称和PermTreeId  测试数据:comment="获取登录用户信息{perm:22}"
func parseMethodNameAndPermTreeId(comment string) (name string, permTreeId uint32) {
	if !strings.Contains(comment, "{perm") {
		return
	}

	comment = strings.TrimRight(comment, "}")
	commentArr := strings.Split(comment, "{perm:")

	if len(commentArr) != 2 {
		return
	}

	name = strings.Trim(commentArr[0], " ")

	id, _ := strconv.Atoi(commentArr[1])
	permTreeId = uint32(id)

	return
}

//从注释中解析出permUrl 测试数据: controller="UserController" action="AuthUser"
func parsePermUrl(controller string, action string) (permUrl string) {
	if controller == "" || action == "" {
		return
	}
	controller = strings.Replace(controller, "Controller", "", 1)

	permUrl = strings.ToLower(controller) + "/" + strings.ToLower(action)
	return
}

//从文件获取带有权限的方法
func parseControllerFiles(file string) (perms []Perm) {

	content, err := ioutil.ReadFile(file)
	if err != nil {
		fmt.Println(err)
		return
	}

	re := regexp.MustCompile(fmt.Sprintf(`//([^\n]+)\nfunc \(this \*([A-Z][A-Z0-9a-z]*Controller)\) ([A-Z][A-Z0-9a-z]*)\(\) \{`))
	res := re.FindAllStringSubmatch(string(content), -1)

	for _, v := range res {
		if len(v) != 4 {
			continue
		}
		name, permTreeId := parseMethodNameAndPermTreeId(v[1])
		//必须要permTreeId 才进行解析
		if permTreeId == 0 {
			continue
		}

		permUrl := parsePermUrl(v[2], v[3])

		perms = append(perms, Perm{
			Name:       name,
			PermtreeId: permTreeId,
			PermUrl:    permUrl,
		})

	}

	return
}

func inArray(s string, ss []string) bool {
	for _, v := range ss {
		if s == v {
			return true
		}
	}

	return false
}

//制造sql
func (this *LocalPermHelp) makeSql() string {
	var (
		databasePerm *Perm
		contentStr   string
	)

	//先对分析出的权限数据排序
	sort.Slice(this.fileParsePerms, func(i, j int) bool {
		return this.fileParsePerms[i].PermUrl < this.fileParsePerms[j].PermUrl
	})

	for _, filePerm := range this.fileParsePerms {
		if databasePerm = this.getPerm(filePerm.PermUrl, this.databasePerms); databasePerm != nil {

			//不需要更新
			if filePerm.Name == databasePerm.Name && filePerm.PermtreeId == databasePerm.PermtreeId {
				continue
			}

			//数据库已经存在的数据
			contentStr += fmt.Sprintf(UpdatePermSql, filePerm.Name, filePerm.PermtreeId, databasePerm.Id)

		} else {
			//新增的数据
			contentStr += fmt.Sprintf(InsertPermSql, filePerm.Name, filePerm.PermUrl, filePerm.PermtreeId)

		}

	}

	return contentStr

}

//生成sql文件
func (this *LocalPermHelp) CreateSql(output string) error {

	contentStr := this.makeSql()

	//输出到文件
	return ioutil.WriteFile(output, []byte(contentStr), 0644)
}

//直接更新本地数据
func (this *LocalPermHelp) UpdateTable() error {

	contentStr := this.makeSql()

	contentArr := strings.Split(contentStr, ";")

	o := orm.NewOrm()
	for _, line := range contentArr {
		line := strings.Trim(line, "\n")
		if line == "" {
			continue
		}
		if _, err := o.Raw(line).Exec(); err != nil {
			return err
		}

	}

	return nil
}

//打印数据库中多余的权限
func (this *LocalPermHelp) PrintDiff() {
	for _, v := range this.databasePerms {
		if perm := this.getPerm(v.PermUrl, this.fileParsePerms); perm == nil {
			fmt.Printf("据库中多余的权限:%+v\n", v)
		}
	}
}
